package com.cdhkppm.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.PrimitiveArrayUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.cdhkppm.constant.TestErrorCodeEnum;
import com.cdhkppm.dto.CustomExcelImportActiveStrDTO;
import com.cdhkppm.service.ExportTaskToExcelService;
import com.cdhkppm.utils.IbaAttributeUtils;
import com.hustcad.plm.pdm.core.base.EntityBase;
import com.hustcad.plm.pdm.core.handle.exception.SystemErrorCodeEnum;
import com.hustcad.plm.rpm.businessdatasecurity.service.ActiveImportDataSecurityHelperBeforeService;
import com.hustcad.plm.rpm.constant.ProjectMngConstant;
import com.hustcad.plm.rpm.model.dto.active.ExcelImportActiveStrDTO;
import com.hustcad.plm.rpm.model.dto.active.ImportActiveRule;
import com.hustcad.plm.rpm.model.dto.active.PlusTaskDTO;
import com.hustcad.plm.rpm.model.dto.active.TyppmActiveTreeDTO;
import com.hustcad.plm.rpm.model.dto.admin.TyppmProjectDTO;
import com.hustcad.plm.rpm.model.entity.plan.TyppmPlan;
import com.hustcad.plm.rpm.model.entity.plan.TyppmPlanActive;
import com.hustcad.plm.rpm.project.service.TyppmProjectService;
import com.hustcad.plm.rpm.service.impl.importdata.TyppmImportDataServiceImpl;
import com.hustcad.plm.rpm.service.intekey.InteKeyService;
import com.hustcad.plm.rpm.service.meta.TyppmEnumerationItemService;
import com.hustcad.plm.rpm.service.plan.TyppmPlanActiveService;
import com.hustcad.plm.rpm.service.plan.TyppmPlanService;
import com.hustcad.plm.rpm.serviceextend.ImportDataExtendService;
import com.hustcad.plm.rpm.util.excel.util.ExcelUtils;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Primary;
import org.springframework.expression.ParseException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigInteger;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author yfq
 * @apiNote
 */

@Slf4j
@Service
@Primary
public class CustomTyppmImportDataServiceImpl extends TyppmImportDataServiceImpl{
    @Resource
    private TyppmPlanService typpmPlanService;
    @Resource
    private TyppmPlanActiveService typpmPlanActiveService;
    @Resource
    private ActiveImportDataSecurityHelperBeforeService activeImportDataSecurityHelperBeforeService;
    @Resource
    private ImportDataExtendService importDataExtendService;

    @Resource
    private InteKeyService inteKeyService;


    @Value("${isEncryption:false}")
    private boolean isEncryption;

    @Value("${tysoft.planActive.checkGroupFlag:'Y'}")
    private String checkGroupFlag;

    private final String afternoonEndHour = "23:59:59";

    @Resource
    private TyppmEnumerationItemService typpmEnumerationItemService;

    @Resource
    private IbaAttributeUtils ibaAttributeUtils;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void exportPlanActiveToExcel(HttpServletRequest request, HttpServletResponse response, String planOid) throws
            IOException {
        TyppmPlan plan = this.typpmPlanService.selectOneByOID(planOid);
        if (Objects.isNull(plan)) {
            throw SystemErrorCodeEnum.ENTITY_NONE.message("项目计划");
        } else {
            Map<String, Object> params = new HashMap<>();
            params.put("rootOID", planOid);
            params.put("rootOTYPE", "ty.projectmng.admin.CTyPlan");
            params.put("markfordelete", 0L);
            List<TyppmActiveTreeDTO> treeDTOList = this.typpmPlanActiveService.queryActiveTreeDTOListByParams(params);
            if (CollUtil.isEmpty(treeDTOList)) {
                throw SystemErrorCodeEnum.DATA_FOUND.message("任务数据");
            } else {
                treeDTOList = (List)treeDTOList.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet(Comparator.comparing(TyppmPlanActive::getLineNumber))), ArrayList::new));
                List<ExcelImportActiveStrDTO> result = this.activeTreeListToExcelImportDto(treeDTOList);
                Map<String, String> fieldMap = initExportPlanColumnTo();
                this.activeImportDataSecurityHelperBeforeService.exportPlanActiveToExcelAfter(treeDTOList, result, fieldMap);
                this.importDataExtendService.exportPlanActiveToExcelAfter(treeDTOList, result, fieldMap);
                Workbook sheets = ExcelUtils.listToExcelWithOutIndex(result, fieldMap);
                DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
                String fileName = plan.getName() + "_" + LocalDateTime.now().format(dtf);
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                try {
                    sheets.write(bos);
                    bos.flush();
                } finally {
                    bos.close();
                }
                byte[] bytes = bos.toByteArray();
                if (PrimitiveArrayUtil.isEmpty(bytes)) {
                    throw SystemErrorCodeEnum.EXCEL_FIELD_MAP_EMPTY.message();
                } else {
                    String srcFileName = fileName + ".xlsx";
                    if (this.isEncryption) {
                        bytes = this.inteKeyService.encrypt(sheets, srcFileName);
                    }

                    this.outputExcel(request, response, bytes, srcFileName);
                }
            }
        }
    }


    @Override
    @GlobalTransactional(rollbackFor = {Exception.class})
    @Transactional(rollbackFor = Exception.class)
    public JSONArray importPlanActiveByExcel(MultipartFile file, String planOid, Map<String, String> fieldMap, Boolean appendFlag){
        try {
            if (StringUtils.isBlank(planOid)) {
                throw SystemErrorCodeEnum.PARAMS_NOT_EMPTY.message("planOid");
            } else {
                //扩展属性
                Map<String, String> extendedFieldMap = new LinkedHashMap<>(fieldMap);
                addExtendFieldMappings(extendedFieldMap);
                this.checkPlanActivesStatue(planOid);
                this.activeImportDataSecurityHelperBeforeService.importPlanActiveByExcelBefore(file, planOid, fieldMap);
                this.importDataExtendService.importPlanActiveByExcelBefore(file, planOid, fieldMap);
                JSONArray array = this.importPlanActiveFromExcel(file, planOid, null, fieldMap, appendFlag);
                this.activeImportDataSecurityHelperBeforeService.importPlanActiveByExcelAfter(array, file, planOid, fieldMap);
                return this.importDataExtendService.importPlanActiveByExcelAfter(array, file, planOid, extendedFieldMap);
            }
        } catch (Throwable var6) {
            throw var6;
        }
    }



    public JSONArray importPlanActiveFromExcel(MultipartFile file, String planOid, TyppmPlanActive planActive, Map<String, String> fieldMap, Boolean appendFlag) {
        try {
            if (!"Y".equalsIgnoreCase(this.checkGroupFlag)) {
                if (fieldMap.containsKey("负责人角色")) {
                    fieldMap.remove("负责人角色");
                }

                if (fieldMap.containsKey("执行人角色")) {
                    fieldMap.remove("执行人角色");
                }
            }

            this.importProjectExcelToRule(fieldMap);
            List<PlusTaskDTO> resultList = Lists.newArrayList();
            List<ExcelImportActiveStrDTO> list = ExcelUtils.excelToList(file, 2, ExcelImportActiveStrDTO.class, fieldMap);
            list = this.excelDataListExcludeNull(list);
            if (!CollectionUtils.isEmpty(list)) {
                ImportActiveRule importRule = new ImportActiveRule();
                importRule.setPlanOid(planOid);
                importRule.setAppendFlag(appendFlag);
                importRule.setAutoComputerEndTime(ProjectMngConstant.IMPORT_RULE_NONE);
                importRule.setPlanActiveOid(Objects.isNull(planActive) ? null : planActive.getOid());
                this.formatImportObjectFinishDateTime(list);
                resultList = this.importPlanActiveByDtoList(list, importRule);
            }

            return JSON.parseArray(JSON.toJSONString(resultList));
        } catch (IllegalAccessException | ParseException | InstantiationException | IOException e) {
            throw TestErrorCodeEnum.TEST_ERROR_CODE_ENUM.getException("Excel导入失败: " + e.getMessage(), e);
        } catch (Exception e) {
            throw TestErrorCodeEnum.TEST_ERROR_CODE_ENUM.getException("导入过程出现未知错误，请联系管理员", e);
        }
    }




    private void formatImportObjectFinishDateTime(List<ExcelImportActiveStrDTO> list) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

        for (ExcelImportActiveStrDTO dto : list) {
            String format;
            if (Objects.nonNull(dto.getFinishDate())) {
                format = dto.getFinishDate().format(formatter);
                format = format.replace("00:00:00", this.afternoonEndHour);
                dto.setFinishDate(LocalDateTime.parse(format, formatter));
            }

            if (Objects.nonNull(dto.getDeadline())) {
                format = dto.getDeadline().format(formatter);
                format = format.replace("00:00:00", this.afternoonEndHour);
                dto.setDeadline(LocalDateTime.parse(format, formatter));
            }
        }

    }

    private List<ExcelImportActiveStrDTO> excelDataListExcludeNull(List<ExcelImportActiveStrDTO> list) {
        List<ExcelImportActiveStrDTO> result = new ArrayList();

        for (ExcelImportActiveStrDTO importActiveStrDTO : list) {
            if (!StringUtils.isBlank(importActiveStrDTO.getIndex()) && !StringUtils.isBlank(importActiveStrDTO.getOutLineNumber()) && !StringUtils.isBlank(importActiveStrDTO.getActiveName())) {
                this.setExcelOutlineNumber(importActiveStrDTO);
                result.add(importActiveStrDTO);
            }
        }

        return result;
    }


    private void setExcelOutlineNumber(ExcelImportActiveStrDTO active) {
        String outLineNumber = active.getOutLineNumber();
        if (outLineNumber.contains(".")) {
            String[] numberArray = outLineNumber.split("\\.");
            if (numberArray.length == NumberUtils.INTEGER_TWO && NumberUtils.INTEGER_ZERO.toString().equals(numberArray[NumberUtils.INTEGER_ONE])) {
                outLineNumber = numberArray[NumberUtils.INTEGER_ZERO];
                active.setOutLineNumber(outLineNumber);
            }
        }

    }
    public void addExtendFieldMappings(Map<String, String> fieldMap) {
        Map<String, String> extendFields = new LinkedHashMap<>();
        extendFields.put("数量", "quantity");
        extendFields.put("工艺段", "processSection");
        extendFields.put("产品单元系统", "productUnitSystem");
        extendFields.put("产品名称", "productName");
        extendFields.put("规格型号", "productModel");
        extendFields.put("产品实现形式", "productForm");
        extendFields.forEach(fieldMap::putIfAbsent);
    }


    public void checkPlanActivesStatue(String planOid) {
        TyppmPlanActive planActive = new TyppmPlanActive();
        planActive.setRootOID(planOid);
        planActive.setRootOTYPE("ty.projectmng.admin.CTyPlan");
        planActive.setMarkForDelete(0L);
        List<TyppmPlanActive> planActives = this.typpmPlanActiveService.simpleQueryListByPlanActive(planActive);
        Optional<TyppmPlanActive> optional = planActives.stream().filter((active) -> ProjectMngConstant.ACTIVE_STATUS_PROCESS.equals(active.getExecutedStatus()) || ProjectMngConstant.ACTIVE_STATUS_NORMAL.equals(active.getExecutedStatus()) || ProjectMngConstant.ACTIVE_STATUS_COMPLETED.equals(active.getExecutedStatus()) || ProjectMngConstant.ACTIVE_STATUS_OVERDUE.equals(active.getExecutedStatus()) || ProjectMngConstant.ACTIVE_STATUS_TERMINATED.equals(active.getExecutedStatus())).findAny();
        if (optional.isPresent()) {
            throw SystemErrorCodeEnum.PLAN_FILE_CANNOT_IMPORT.message();
        }
    }

    public static Map<String, String> initExportPlanColumnTo() {
        LinkedHashMap<String, String> fieldMap = new LinkedHashMap<>();
        fieldMap.put("index", "序号");
        fieldMap.put("wbsNumber", "WBS编号");
        fieldMap.put("outLineNumber", "层级");
        fieldMap.put("activeName", "任务名称");
        fieldMap.put("quantity", "数量");
        fieldMap.put("processSection", "工艺段");
        fieldMap.put("productUnitSystem", "产品单元系统");
        fieldMap.put("productName", "产品名称");
        fieldMap.put("productModel", "规格型号");
        fieldMap.put("productForm", "产品实现形式");
        fieldMap.put("startDate", "计划开始时间");
        fieldMap.put("finishDate", "计划结束时间");
        fieldMap.put("deadline", "最晚完成时间");
        fieldMap.put("period", "计划工时(小时)");
        fieldMap.put("actualPeriod", "实际工时(小时)");
        fieldMap.put("milestone", "里程碑");
        fieldMap.put("controlled", "受控");
        fieldMap.put("beforeLineNumber", "前置任务");
        fieldMap.put("ownerGroupDisplayName", "负责人角色");
        fieldMap.put("ownerName", "负责人");
        fieldMap.put("executorGroupDisplayName", "执行人角色");
        fieldMap.put("executorName", "执行人");
        return fieldMap;
    }

    public static Map<String, String> initExportPlanColumnToExcel() {
        LinkedHashMap<String, String> fieldMap = new LinkedHashMap<>();
        fieldMap.put("index", "序号");
        fieldMap.put("outLineNumber", "层级");
        fieldMap.put("activeName", "任务名称");
//        fieldMap.put("quantity", "数量");
//        fieldMap.put("processSection", "工艺段");
//        fieldMap.put("productUnitSystem", "产品单元系统");
//        fieldMap.put("productName", "产品名称");
//        fieldMap.put("productModel", "规格型号");
//        fieldMap.put("productForm", "产品实现形式");
        fieldMap.put("isAssigned", "是否已分配");
//        fieldMap.put("ownerName", "负责人");
//        fieldMap.put("startDate", "计划开始时间");
//        fieldMap.put("finishDate", "计划结束时间");
        fieldMap.put("devLeader", "设计执行人");
        fieldMap.put("devPlanStartTime", "计划开始时间");
        fieldMap.put("devPlanEndTime", "计划结束时间");
        fieldMap.put("mfgLeader", "制造执行人");
        fieldMap.put("mfgPlanStartTime", "计划开始时间");
        fieldMap.put("mfgPlanEndTime", "计划结束时间");
        fieldMap.put("purchaseLeader", "外购执行人");
        fieldMap.put("purchasePlanStartTime", "计划开始时间");
        fieldMap.put("purchasePlanEndTime", "计划结束时间");


//        fieldMap.put("period", "计划工时(小时)");
//        fieldMap.put("actualPeriod", "实际工时(小时)");
//        fieldMap.put("milestone", "里程碑");
//        fieldMap.put("controlled", "受控");
//        fieldMap.put("beforeLineNumber", "前置任务");
//        fieldMap.put("ownerGroupDisplayName", "负责人角色");
//
//        fieldMap.put("executorGroupDisplayName", "执行人角色");
//        fieldMap.put("executorName", "执行人");
        return fieldMap;
    }


    private void outputExcel(HttpServletRequest request, HttpServletResponse response, byte[] bytes, String fileName) throws IOException {
        OutputStream outputStream = null;

        try {
            outputStream = ExcelUtils.getExportOutStream(request, response, fileName);
            outputStream.write(bytes);
            outputStream.flush();
            outputStream.close();
        } catch (Exception var10) {
            throw SystemErrorCodeEnum.INTERNAL_SERVER_ERROR.message();
        } finally {
            if (Objects.nonNull(outputStream)) {
                outputStream.flush();
                outputStream.close();
            }

        }

    }

    private List<ExcelImportActiveStrDTO> activeTreeListToExcelImportDto(List<TyppmActiveTreeDTO> treeDTOList) {
        List<ExcelImportActiveStrDTO> result = new ArrayList<>();
        if (CollUtil.isEmpty(treeDTOList)) {
            return result;
        } else {
            Map activeTreeDTOMap = treeDTOList.stream().collect(Collectors.toMap(
                    EntityBase::getOid, Function.identity(), (a, b) -> a));
            Map<String, String> roleMap = this.typpmEnumerationItemService.getEnumDisplayByDefAndItemKey("role", "");

            for(int i = 0; i < treeDTOList.size(); ++i) {
                TyppmActiveTreeDTO treeDTO = treeDTOList.get(i);
                CustomExcelImportActiveStrDTO excelDTO = new CustomExcelImportActiveStrDTO();
                this.copyActiveDtoAttributeToExcelDto(excelDTO, treeDTO, roleMap);
                excelDTO.setIndex(String.valueOf(i + NumberUtils.INTEGER_ONE));
                excelDTO.setBeforeLineNumber(this.getActiveFormatBeforeLineNumber(treeDTO, activeTreeDTOMap));
                result.add(excelDTO);
            }

            return result;
        }
    }


    private void copyActiveDtoAttributeToExcelDto(CustomExcelImportActiveStrDTO excelDTO, TyppmActiveTreeDTO treeDTO, Map<String, String> roleMap) {
        excelDTO.setActiveName(treeDTO.getName());
        excelDTO.setOwnerGroupDisplayName((String)roleMap.get(treeDTO.getOwnerGroupKey()));
        String executorName = treeDTO.getExecutorName();
        if (CharSequenceUtil.isNotBlank(executorName) && executorName.indexOf("(") >= 0 && executorName.indexOf(")") >= 0) {
            executorName = executorName.substring(executorName.indexOf("(") + 1, executorName.lastIndexOf(")"));
        }

        excelDTO.setExecutorName(executorName);
        excelDTO.setExecutorGroupDisplayName((String)roleMap.get(treeDTO.getExecutorGroupKey()));
        excelDTO.setOwnerName(treeDTO.getOwnerName());
        excelDTO.setOutLineLevel(String.valueOf(treeDTO.getOutLineLevel()));
        excelDTO.setOutLineNumber(treeDTO.getOutLineNumber());
        excelDTO.setWbsNumber(treeDTO.getWbs());
        excelDTO.setControlled(treeDTO.getControlled());
        excelDTO.setMilestone(treeDTO.getMilestone());
        excelDTO.setDeadline(treeDTO.getDeadline());
        excelDTO.setStartDate(treeDTO.getStartDate());
        excelDTO.setFinishDate(treeDTO.getFinishDate());
        long hour;
        if (Objects.nonNull(treeDTO.getRatedHours())) {
            hour = treeDTO.getRatedHours() / 3600000L;
            excelDTO.setPeriod(String.valueOf(hour));
        }

        if (Objects.nonNull(treeDTO.getDoneEffortMillis()) && 0L != treeDTO.getDoneEffortMillis()) {
            hour = treeDTO.getDoneEffortMillis() / 3600000L;
            excelDTO.setActualPeriod(String.valueOf(hour));
        }
        String oid = treeDTO.getJobOID();
        String otype = treeDTO.getJobOTYPE();
        excelDTO.setSecurityLabels(treeDTO.getSecurityLabels());
        excelDTO.setQuantity(ibaAttributeUtils.getSingleAttrValue(new BigInteger(oid), otype, "quantity"));
        excelDTO.setProcessSection(ibaAttributeUtils.getSingleAttrValue(new BigInteger(oid), otype, "processSection"));
        excelDTO.setProductUnitSystem(ibaAttributeUtils.getSingleAttrValue(new BigInteger(oid), otype, "productUnitSystem"));
        excelDTO.setProductName(ibaAttributeUtils.getSingleAttrValue(new BigInteger(oid), otype, "productName"));
        excelDTO.setProductModel(ibaAttributeUtils.getSingleAttrValue(new BigInteger(oid), otype, "productModel"));
        excelDTO.setProductForm(ibaAttributeUtils.getSingleAttrValue(new BigInteger(oid), otype, "productForm"));
    }


    private String getActiveFormatBeforeLineNumber(TyppmActiveTreeDTO treeDTO, Map<String, TyppmActiveTreeDTO> activeTreeDTOMap) {
        String splitChar = ",";
        String result = "";
        if (CharSequenceUtil.isBlank(treeDTO.getBeforeOID())) {
            return result;
        } else {
            List<String> resultList = new ArrayList();
            String[] oidSplit = treeDTO.getBeforeOID().split(splitChar);
            long[] durationSplit = CharSequenceUtil.splitToLong(treeDTO.getBeforeDurationMillis(), splitChar);
            long[] typeSplit = CharSequenceUtil.splitToLong(treeDTO.getBeforePrecedenceType(), splitChar);

            for(int i = 0; i < oidSplit.length; ++i) {
                String oid = oidSplit[i];
                TyppmActiveTreeDTO dto = (TyppmActiveTreeDTO)activeTreeDTOMap.get(oid);
                String lineNumber = String.valueOf(dto.getLineNumber());
                Long duration = 0L;
                if (durationSplit.length > i) {
                    long l = durationSplit[i] / 28800000L;
                    duration = l;
                }

                String type = ProjectMngConstant.getPrecedenceTypeValueMap(ProjectMngConstant.PRECEDENCE_TYPE_FS).toUpperCase();
                if (typeSplit.length > i) {
                    String precedenceTypeValue = ProjectMngConstant.getPrecedenceTypeValueMap(typeSplit[i]);
                    if (StringUtils.isNotBlank(precedenceTypeValue)) {
                        type = precedenceTypeValue.toUpperCase(Locale.US);
                    }
                }

                resultList.add(this.formatBeforeLineString(lineNumber, duration, type));
            }

            return CharSequenceUtil.join(",", resultList);
        }
    }

    private String formatBeforeLineString(String lineNumber, Long duration, String type) {
        String op = "";
        if (duration > 0L) {
            op = "+" + Math.abs(duration);
        } else if (duration < 0L) {
            op = "-" + Math.abs(duration);
        }

        return lineNumber + type + op;
    }
}
